package com.wanxi.springboot.team.manage.system.service.impl;

import com.wanxi.springboot.team.manage.system.api.CommonResult;
import com.wanxi.springboot.team.manage.system.esModel.EsUser;
import com.wanxi.springboot.team.manage.system.mapper.RoleMapper;
import com.wanxi.springboot.team.manage.system.model.*;
import com.wanxi.springboot.team.manage.system.mapper.UserMapper;
import com.wanxi.springboot.team.manage.system.repository.LogRepository;
import com.wanxi.springboot.team.manage.system.repository.UserRepository;
import com.wanxi.springboot.team.manage.system.service.LogService;
import com.wanxi.springboot.team.manage.system.service.UserRoleService;
import com.wanxi.springboot.team.manage.system.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wanxi.springboot.team.manage.system.util.JwtTokenUtil;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.*;
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.File;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 蒋帆
 * @since 2020-12-21
 */
@Service
@CacheConfig(cacheNames = "teamManageSystem::user")
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    @Autowired(required = false)
    UserMapper userMapper;
    @Autowired
    PasswordEncoder passwordEncoder;
    @Autowired
    JwtTokenUtil jwtTokenUtil;
    @Value("${jwt.tokenHead}")
    String tokenHead;
    @Autowired
    RedisServiceImpl redisService;
    @Autowired
    ElasticsearchRestTemplate elasticsearchRestTemplate;
    @Autowired
    UserRepository userRepository;
    @Autowired
    LogService logService;
    @Autowired
    LogRepository logRepository;
    @Autowired
    UserRoleService userRoleService;
    @Autowired(required = false)
    RoleMapper roleMapper;

    @Override
    public CommonResult getUserByName(String username) {//根据用户名获取用户
        User user = null;//判断缓存是否有该用户
        if (!redisService.hasKey("teamManageSystem::user::" + username))
            user = userMapper.getUserByName(username);//如果没有，访问数据库
        else//如果有，则直接从缓存中拿到该值
            user = (User) redisService.get("teamManageSystem::user::" + username);
        return CommonResult.success(user);
    }

    @Override//获取该用户所拥有的权限
    public List<Permission> getPermissionsByUserId(Integer id) {
        return userMapper.getPermissionsByUserId(id);
    }

    @Override//用户注册
    public CommonResult register(User user) {
        //将密码加密
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        //设置用户的初始值
        user.setEnable("yes");
        user.setStatus("normal");
        //添加用户
        int result = userMapper.register(user);
        if (result > 0) {//如果添加成功
            if (user.getRole() != null) {//判断用户是否选择了角色
                //如果选择了角色，则将用户角色添加到用户角色表中
                UserRole userRole = new UserRole();
                userRole.setRole(user.getRole()).setUser(user.getId());
                userRoleService.save(userRole);
            }
            //生成该用户信息缓存到Redis
//            redisService.set("teamManageSystem::user::" + user.getCode(), user);
            //删除用户信息列表
            redisService.del("teamManageSystem::user::getUsers");
            return CommonResult.success(user, "注册成功！");
        }
        return CommonResult.failed("注册失败");
    }

    /**
     * 用户登录
     *
     * @param user
     * @return
     */
    @Override
    public CommonResult login(User user) {
        String token = null;
        HashMap<String, Object> data = new HashMap<>();
        User usr = null;
        //如果没有该用户冻结标识,则允许进行登录操作
        if (!redisService.hasKey("teamManageSystem::user::" + user.getCode() + "::frozen")) {
            try {
                //判断Redis是否有该用户信息
                if (!redisService.hasKey("teamManageSystem::user::" + user.getCode()))
                    usr = (User) getUserByName(user.getCode()).getData();//没有则从数据库取值
                else//Redis中有该键值则从Redis中取值
                    usr = (User) redisService.get("teamManageSystem::user::" + user.getCode());
                //判断输入的密码是否与数据库相等
                boolean matches = passwordEncoder.matches(user.getPassword(), usr.getPassword());
                if (matches) {//如果相等则生成token
                    token = jwtTokenUtil.generateToken(usr);
                } else
                    token = null;
            } catch (Exception e) {
                e.printStackTrace();
                return CommonResult.validateFailed("用户名或密码错误");
            }
            //没有token则记录用户错误次数
            if (!StringUtils.hasLength(token)) {
                return recordErrorToRedis(user.getCode());
            }
            //封装用户头像
            //如果用户头像值不为空
            if (usr.getIcon() != null && usr.getIcon() != "") {
                //判断是否是base64格式
                //先去掉逗号以前的数据头
                int index = usr.getIcon().indexOf(",");//获得逗号的索引值
                if (index == -1)//如果没找到该索引
                    usr.setIcon("http://t.cn/RCzsdCq");//则直接设置默认头像
                else {//新建base64编码正则表达式
                    String base64Pattern = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$";
                    boolean match = Pattern.matches(base64Pattern, usr.getIcon().substring(index + 1));//判断是否符合正则表达式
                    if (!match)//如果不匹配，则设置为默认头像
                        usr.setIcon("http://t.cn/RCzsdCq");
                }
            } else
                usr.setIcon("http://t.cn/RCzsdCq");
            //获取用户权限
            List<Permission> permissionList = userMapper.getPermissionsByUserId(usr.getId());
            HashSet<Permission> permissions = new HashSet<>(permissionList);
            usr.setAuthorities(permissions);
            //获取用户角色
            Role role = roleMapper.getRoleByUserId(usr.getId());
            if (role!=null) {
                usr.setRole(role.getId());
                usr.setRoleName(role.getName());
            }
//            打印登录日志
//            logging(user.getCode());
            //将token传入前台
            data.put("userInfo", usr);
            data.put("tokenHead", tokenHead);
            data.put("access_token", token);
            //清除用户的密码错误次数记录
            clearRedisErrorCode(user.getCode());
            //将用户登录信息存入缓存
            redisService.set("teamManageSystem::user::" + user.getCode(), usr, 86400);
            return CommonResult.success(data, "登录成功！");
        } else {//如果有该用户冻结标识，则直接反馈冻结信息给用户
            long expire = redisService.getExpire("teamManageSystem::user::" + user.getCode() + "::frozen");
            long frozenHours = expire/3600;
            long frozenMinutes = expire%3600/60;
            long frozenSeconds = expire%3600%60;
            return CommonResult.validateFailed("该账号已被冻结，请"+frozenHours+"小时"+frozenMinutes+"分"+frozenSeconds+"秒后再试");
        }
    }

    /**
     * 获取用户列表
     *
     * @param map
     * @return
     */
    @Override
//    @Cacheable(keyGenerator = "keyGenerator")
    public CommonResult getUsers(Map map) {
        int page = (Integer.parseInt((String) map.get("page")) - 1) * Integer.parseInt((String) map.get("limit"));
        map.put("page", page);
        map.put("limit", Integer.parseInt((String) map.get("limit")));
        List<User> users = userMapper.getUsers(map).stream().map(u -> {
            //封装用户状态（是否冻结）
            if (redisService.hasKey("teamManageSystem::user::" + u.getCode() + "::frozen"))
                u.setStatus("<input type='checkbox' value='" + u.getCode() + "' name='status' lay-filter='status' lay-skin='switch' lay-text='正常|冻结'>");
            else
                u.setStatus("<input type='checkbox' value='" + u.getCode() + "' name='status' lay-filter='status' lay-skin='switch' lay-text='正常|冻结' checked>");
            //封装用户头像
            File file = new File("E:/IdeaWorksplace/team_manage_system/src/main/resources/static" + u.getIcon());
            if (!file.exists()) {
                if (u.getIcon() != null && u.getIcon() != "") {
                    //判断是否是base64
                    int index = u.getIcon().indexOf(",");
                    if (index == -1)
                        u.setIcon("http://t.cn/RCzsdCq");
                    else {
                        String base64Pattern = "^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$";
                        boolean match = Pattern.matches(base64Pattern, u.getIcon().substring(index + 1));
                        if (!match)
                            u.setIcon("http://t.cn/RCzsdCq");
                    }
                } else
                    u.setIcon("http://t.cn/RCzsdCq");
            }
            //封装用户角色
            Role role = roleMapper.getRoleByUserId(u.getId());
            if (role != null) {
                u.setRole(role.getId());
                u.setRoleName(role.getName());
            }
            return u;
        }).collect(Collectors.toList());
        long count = userMapper.getUserCount(map);
        return CommonResult.success(users, count);
    }

    /**
     * 删除用户
     *
     * @param code
     * @return
     */
    @Override
    public CommonResult deleteUser(String code) {
        int result = userMapper.deleteUser(code);
        if (result > 0) {
            redisService.del("teamManageSystem::user::" + code);
            return CommonResult.success(result, "删除成功");
        }
        return CommonResult.failed("删除失败");
    }

    /**
     * 修改用户
     *
     * @param user
     * @return
     */
    @Override
    public CommonResult updateUser(User user) {
        if (user.getPassword() != "") {//如果密码不为空，
            user.setPassword(passwordEncoder.encode(user.getPassword()));//将密码加密
        }//修改用户信息
        int result = userMapper.updateUser(user);
        if (result > 0) {//如果修改成功
            if (user.getRole() != null) {//如果选择了用户角色
                UserRole userRole = userRoleService.getUserRoleByUser(user.getId());//查找数据库用户角色表是否有该用户角色信息
                if (userRole == null) {//如果没有，则添加
                    userRole = new UserRole();
                    userRole.setRole(user.getRole()).setUser(user.getId());
                }
                userRoleService.saveOrUpdate(userRole);
            }//修改成功后删除Redis缓存中的用户列表和该修改后用户的数据，以便下次刷新
            redisService.del("teamManageSystem::user::getUsers", "teamManageSystem::user::" + user.getCode());
//            redisService.set("teamManageSystem::user::" + user.getCode(), user,86400);
            return CommonResult.success(user, "修改成功！");
        }
        return CommonResult.failed("修改失败");
    }

    /**
     * 获取码表信息
     *
     * @return
     */
    @Override
    public CommonResult getCodeList() {
        HashMap data = new HashMap();
        List<Code> codeList = null;
        codeList = userMapper.getCodeList();
        List<Code> sex = new ArrayList<>();
        List<Code> graduate = new ArrayList<>();
        List<Code> enable = new ArrayList<>();
        List<Code> userStatus = new ArrayList<>();
        List<Code> permissionType = new ArrayList<>();
        List<Code> signStatus = new ArrayList<>();
        List<Code> dailySummaryEditStatus = new ArrayList<>();
        List<Code> dailySummaryReadStatus = new ArrayList<>();
        //将码表分类存放
        for (Code code : codeList) {
            if (code.getType().equals("sex"))
                sex.add(code);
            else if (code.getType().equals("graduate"))
                graduate.add(code);
            else if (code.getType().equals("enable"))
                enable.add(code);
            else if (code.getType().equals("user_status"))
                userStatus.add(code);
            else if (code.getType().equals("permission_type"))
                permissionType.add(code);
            else if (code.getType().equals("sign_status"))
                signStatus.add(code);
            else if (code.getType().equals("daily_summary_edit_status"))
                dailySummaryEditStatus.add(code);
            else if (code.getType().equals("daily_summary_read_status"))
                dailySummaryReadStatus.add(code);
        }
        data.put("sex", sex);
        data.put("graduate", graduate);
        data.put("enable", enable);
        data.put("user_status", userStatus);
        data.put("permission_type", permissionType);
        data.put("sign_status", signStatus);
        data.put("daily_summary_edit_status", dailySummaryEditStatus);
        data.put("daily_summary_read_status", dailySummaryReadStatus);
        return CommonResult.success(data);
    }

    /**
     * 更新用户状态（冻结/解冻）
     *
     * @param code
     * @param status
     * @return
     */
    @Override
    public CommonResult updateStatus(String code, String status) {
        if (status.equals("frozen")) {
            redisService.set("teamManageSystem::user::" + code + "::frozen", true);
            return CommonResult.success("frozen");
        } else {
            redisService.del("teamManageSystem::user::" + code + "::frozen");
            return CommonResult.success("normal");
        }
    }

    /**
     * 获取组长信息
     *
     * @return
     */
    @Override
    public CommonResult getLeaderList() {
        List<User> users = userMapper.getLeaderList();
        return CommonResult.success(users);
    }

    /**
     * 获取教练信息
     *
     * @return
     */
    @Override
    public CommonResult getCoachList() {
        List<User> users = userMapper.getCoachList();
        return CommonResult.success(users);
    }

    /**
     * 获取小组用户
     *
     * @param teamId
     * @return
     */
    @Override
    public CommonResult getTeamUser(Integer teamId) {
        List<User> users = userMapper.getTeamUser(teamId);
        List<User> teamMember = userMapper.getInTeamMembers(teamId);
        List<Integer> collect = teamMember.stream().map(m -> m.getId()).collect(Collectors.toList());
        HashMap data = new HashMap();
        data.put("members", users);
        data.put("inTeamMember", teamMember);
        data.put("inTeamMemberIds", collect);
        return CommonResult.success(data);
    }

    /**
     * 获取小组成员
     *
     * @param teamId
     * @return
     */
    @Override
    public CommonResult getTeamMember(Integer teamId) {
        List<User> teamMember = userMapper.getTeamMember(teamId);
        return CommonResult.success(teamMember);
    }

    /**
     * 通过该用户id获取该小组成员信息
     *
     * @param userId
     * @return
     */
    @Override
    public CommonResult getTeamMemberByUser(Integer userId) {
        List<User> users = userMapper.getTeamMemberByUser(userId);
        return CommonResult.success(users);
    }

    /**
     * 获取该用户小组中未签到的成员信息
     *
     * @param userId
     * @return
     */
    @Override
    public CommonResult getNoSignTeamMemberByUser(Integer userId) {
        List<User> users = userMapper.getNoSignTeamMemberByUser(userId);
        return CommonResult.success(users);
    }

    /**
     * 记录用户输入登录信息错误次数
     */
    public CommonResult recordErrorToRedis(String username) {
        //判断用户是否有输入错误次数
        if (redisService.hasKey("teamManageSystem::user::" + username + "::errorCount")) {
            //如果有，则获取输入错误的次数
            int count = (int) redisService.get("teamManageSystem::user::" + username + "::errorCount");
            count++;//将输入错误次数加1
            if (count >= 3) {//如果加1后的值大于或等于3，则生成一个冻结标识到Redis，并设定失效时间为12小时
                redisService.set("teamManageSystem::user::" + username + "::frozen", true, 43200);
                redisService.expire("teamManageSystem::user::" + username + "::frozen", 43200);
                redisService.del("teamManageSystem::user::" + username + "::errorCount");
                return CommonResult.validateFailed("您已输入错误三次，账号已被冻结，请12小时后再试");
            } else {
                redisService.expireAt("teamManageSystem::user::" + username + "::errorCount", count, 10800);
            }
        } else {//如果之前没有，则生成一个错误次数缓存到Redis，并设定失效时间为3小时内
            redisService.set("teamManageSystem::user::" + username + "::errorCount", 1, 10800);
            redisService.expire("teamManageSystem:user::" + username + "::errorCount", 10800);
//                redisService.expireAt("teamManageSystem:user:" + username + ":errorCount", 1, 10800);
        }
        return CommonResult.validateFailed("用户名或密码错误");
    }

    /**
     * 清除错误计数
     */
    private void clearRedisErrorCode(String username) {
        redisService.del("teamManageSystem::user::" + username + "::errorCount", "teamManageSystem::user::" + username + "::frozen");
    }

    /**
     * 将用户存入到elastic search
     */
    private void saveUserToEs() {
        List<EsUser> users = userMapper.getUserList().stream().map(u -> {
            File file = new File("E:/IdeaWorksplace/team_manage_system/src/main/resources/static" + u.getIcon());
            if (!file.exists())
                u.setIcon("http://t.cn/RCzsdCq");
            EsUser esUser = new EsUser();
            BeanUtils.copyProperties(u, esUser);
            return esUser;
        }).collect(Collectors.toList());
        userRepository.saveAll(users);
    }

    /**
     * 存储用户登录日志
     *
     * @param username
     */
//    private void logging(String username) {
//        Log log = new Log();
//        //格式化登录时间
//        String sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
//        //获取IP地址
//        InetAddress addr = null;
//        try {
//            addr = InetAddress.getLocalHost();
//        } catch (UnknownHostException e) {
//            e.printStackTrace();
//        }
//        String ip = addr.getHostAddress();//获得本机IP
//        String address = addr.getHostName();//获得本机名称
//        log.setUserCode(username).setLoginTime(sdf).setIpAddress(ip + " " + address);
////        将日志存入数据库
//        logService.save(log);
//        EsLog esLog = new EsLog();
//        BeanUtils.copyProperties(log, esLog);//深拷贝一份到esLog
////        将登录日志存入到elasticsearch
//        try {
//            logRepository.save(esLog);
//        } catch (Exception e) {
//            System.out.println("elasticsearch未开启");
//        }
//
//    }
}
